import re
import string
import unicodedata
from collections import Counter

def match(prediction:str, ground_truth:list[str])->int:
    '''
    this match method copy selfrag soure code directly.()
    '''
    for gt in ground_truth:
        if gt in prediction: 
            return 1
    return 0

# See: https://github.com/hotpotqa/hotpot/blob/master/hotpot_evaluate_v1.py

def exact_match(prediction:str, answers_list: list[str]):
    assert type(answers_list) == list
    return max(em_score(prediction, ans) for ans in answers_list)

def normalize_text(s):
    s = unicodedata.normalize('NFD', s)

    def remove_articles(text):
        return re.sub(r'\b(a|an|the)\b', ' ', text)

    def white_space_fix(text):
        return ' '.join(text.split())

    def remove_punc(text):
        exclude = set(string.punctuation)
        return ''.join(ch for ch in text if ch not in exclude)

    def lower(text):
        return text.lower()

    return white_space_fix(remove_articles(remove_punc(lower(s))))

def em_score(prediction:str, ground_truth:str):
    return normalize_text(prediction) == normalize_text(ground_truth)

def F1(prediction, answers_list):
    assert type(answers_list) == list
    return max(f1_score(prediction, ans) for ans in answers_list)

def f1_score(prediction:str, ground_truth:str)->float:
    prediction_tokens = normalize_text(prediction).split()
    ground_truth_tokens = normalize_text(ground_truth).split()

    common = Counter(prediction_tokens) & Counter(ground_truth_tokens)
    num_same = sum(common.values())

    if len(prediction_tokens) == len(ground_truth_tokens) == 0:
        # Unlike most tasks, QReCC and SQuAD-2.0 assign 1.0 in this edge case. We don't for uniformity.
        # print("\n#> F1 Metric: Rare edge case of len(prediction_tokens) == len(ground_truth_tokens) == 0.\n")
        pass
    if num_same == 0:
        return 0

    precision = 1.0 * num_same / len(prediction_tokens)
    recall = 1.0 * num_same / len(ground_truth_tokens)
    f1 = (2 * precision * recall) / (precision + recall)

    return f1

def HotPotF1(prediction, answers_list):
    assert type(answers_list) == list

    return max(hotpot_f1_score(prediction, ans) for ans in answers_list)

def hotpot_f1_score(prediction, ground_truth):
    normalized_prediction = normalize_text(prediction)
    normalized_ground_truth = normalize_text(ground_truth)

    if normalized_prediction in ['yes', 'no', 'noanswer'] and normalized_prediction != normalized_ground_truth:
        return 0
    if normalized_ground_truth in ['yes', 'no', 'noanswer'] and normalized_prediction != normalized_ground_truth:
        return 0

    prediction_tokens = normalized_prediction.split()
    ground_truth_tokens = normalized_ground_truth.split()
    common = Counter(prediction_tokens) & Counter(ground_truth_tokens)
    num_same = sum(common.values())
    if num_same == 0:
        return 0
    precision = 1.0 * num_same / len(prediction_tokens)
    recall = 1.0 * num_same / len(ground_truth_tokens)
    f1 = (2 * precision * recall) / (precision + recall)
    return f1